# -*-coding:utf-8-*-
# 12.4 属性访问
class C:
    def __init__(self):
        self.x = 'X-man'


c = C()


# print(c.x)
# print(getattr(c, 'x', '木有这个属性'))
# print(getattr(c, 'y', '木有这个属性'))
# setattr(c, 'y', 'Yellow')
# print(getattr(c, 'y', '木有这个属性'))
# delattr(c, 'x')


class C:
    def __init__(self, size=10):
        self.size = size

    def getSize(self):
        return self.size

    def setSize(self, value):
        self.size = value

    def delSize(self):
        del self.size

    x = property(getSize, setSize, delSize)


# c = C()
# print(c.x)
# c.x =12
# print(c.x)
# print(c.size)
# del c.x
# c.size


class C:
    def __getattribute__(self, name):
        print('getattribute')
        # 使用super（） 调用object基类的__getattribute__方法
        return super().__getattribute__(name)

    def __setattr__(self, name, value):
        print('setattr')
        super().__setattr__(name, value)

    def __delattr__(self, name):
        print('delattr')
        super().__delattr__(name)

    def __getattr__(self, name):
        print('getattr')


# c = C()
# c.x
# c.x = 1
# c.x
# del c.x
# setattr(c, 'y', 'Yellow')


class Rectangle:
    def __init__(self, width=0, height=0):
        self.width = width
        self.height = height

    def __setattr__(self, name, value):
        if name == 'square':
            self.width = value
            self.height = value
        else:
            # super（） 来调用基类的__setattr__()魔法方法
            super().__setattr__(name, value)

    def getArea(self):
        return self.width * self.height


r1 = Rectangle(4, 5)
# print(r1.getArea())
r1.square = 10


# print(r1.getArea())

# 12.5 描述符（property的原理）
class MyDecriptor:
    def __get__(self, instance, owner):
        print("getting...", self, instance, owner)

    def __set__(self, instance, value):
        print("setting...", self, instance, value)

    def __delete__(self, instance):
        print("detleting...", self, instance)


class Test:
    x = MyDecriptor()


test = Test()


# test.x
# test.x = 'X-man'
# del test.x


class MyPropery:
    def __init__(self, fget=None, fset=None, fdel=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel

    def __get__(self, instance, owner):
        return self.fget(instance)

    def __set__(self, instance, value):
        return self.fset(instance, value)

    def __delete__(self, insatance):
        self.fdel(insatance)


class C:
    def __init__(self):
        self._x = None

    def getX(self):
        return self._x

    def setX(self, value):
        self._x = value

    def delX(self):
        del self._x


# x = MyPropery(getX, setX, delX)

c = C()
c.x = 'X-man'
c._x
del c.x


class Celsius:
    def __init__(self, value=26.0):
        self.value = float(value)

    def __get__(self, instance, owner):
        return self.value

    def __set__(self, instance, value):
        self.value = float(value)


class Fahrenhetic:
    def __get__(self, instance, owner):
        return instance.cel * 1.8 + 32

    def __set__(self, instance, value):
        instance.cel = (float(value) - 32) / 1.8


class Temperature:
    cel = Celsius()

    fah = Fahrenhetic()


# temp = Temperature()
# print(temp.cel)
# print(temp.fah)

# 12.6 定制序列
# 鸭子类型 动态类型的一种风格 http://bbs.fishc.com/thread-51471-1-1.html

# 一个对象的有效语义，不是由继承自特定的类或则实现特定的接口，而是由当前方法和属性的集合决定。
# “当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来像鸭子，那么这只鸟就可以被称为鸭子。”

# 在鸭子类型中，关注的不是对象的类型本身，而是它是如何使用的。

# 鸭子类型通常得益于不测试方法和函数中参数的类型，而是依赖文档、清晰的代码和测试来确保正确使用。

# 从静态类型语言转向动态类型语言的用户通常试图添加一些静态的（在运行之前的）类型检查，从而影响了鸭子类型的益处和可伸缩性，
# 并约束了语言的动态特性（Python文档中有一句：鸭子类型应避免使用type（）或instance（）等测试类型是否合法）。
class Duck:
    def quack(self):
        print("呱呱呱！")

    def feathers(self):
        print("这个鸭子拥有灰白灰白的羽毛。")


class Person:
    def quack(self):
        print("你才是鸭子你们全家人都是鸭子！")

    def feathers(self):
        print("这个人穿着一件羽绒大衣。")


def in_the_forest(duck):
    duck.quack()
    duck.feathers()


def game():
    donald = Duck()
    john = Person()
    in_the_forest(donald)
    in_the_forest(john)


# game()


# 像序列类型（如列表、元组、字符串）或映射类型（如字典）都是属于容器类型
# ·容器不可变，只需要定义__len__()和__getitem__()方法
# ·容器可变，除了__len__()和__getitem__()方法，还需要定义__setitem__()和__delitem__()两个方法
class CountList:
    def __init__(self, *args):
        self.values = [x for x in args]
        self.count = {}.fromkeys(range(len(self.values)), 0)
        # 这里使用列表的下标作为字典的键，注意不能用元素作为字典的键
        # 因为列表的不同下标可能有值一样的元素，但字典不能有两个相同的键

    def __len__(self):
        return len(self.values)

    def __getitem__(self, key):
        self.count[key] += 1
        return self.values[key]


# c1 = CountList(1, 3, 5, 7, 9)
# c2 = CountList(2, 4, 6, 8, 10)
# print(c1[1])
# print(c2[1])
# print(c1[1] + c2[1])
# print(c1.count)
# print(c2.count)

# 12.7 迭代器
# 通常接触的迭代器有序列(列表、元组、字符串)还有字典也是迭代器
# for i in "Fishc":
# print (i)

# 字典和文件也是支持迭代操作的
links = {'鱼C工作室': 'http://www.fishc.com', '鱼C论坛': 'http://bbs.fishc.com',
         '鱼C博客': 'http://blog.fishc.com', '支持小甲鱼': 'http://fishc.taobao.com'}

# for each in links:
#     print('%s->%s' % (each, links[each]))
# 注：对一个容器调用iter()就得到它的迭代器，调用next()迭代器就会返回下一个值，没有值，抛出StopIteration异常

string = "Fishc"
it = iter(string)
# print(next(it))
# print(next(it))
# print(next(it))
# print(next(it))
# print(next(it))
# print(next(it))
# 分析 for语句工作原理
string = "FishC"
it = iter(string)
while True:
    try:
        each = next(it)
    except StopIteration:
        break
    # print(each)


# 一个容器如果是迭代器，那就必须实现__iter()__魔法方法，这个方法实际上就是返回迭代器本身。
# __next__()魔法方法，决定了迭代的规则。
class Fibs:
    def __init__(self):
        self.a = 0
        self.b = 1

    def __iter__(self):
        return self

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        return self.a


# fibs = Fibs()
# for each in fibs:
#     if each < 20:
#         print(each)
#     else:
#         break
# 注：没有终点，如果没有跳出循环，会不断迭代下去，可不可以加一个参数，用于控制迭代的范围呢？
class Fibs:
    def __init__(self, n=20):
        self.a = 0
        self.b = 1
        self.n = n

    def __iter__(self):
        return self

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        if self.a > self.n:
            raise StopIteration
        return self.a


# fibs = Fibs()
# fibs = Fibs(10)
# for each in fibs:
#     print(each)


# 12.8 生成器（乱入）
# 迭代器需要我们自己定义一个类和实现相关的方法，而生成器则只需要在普通的函数中加上一个yield语句
# 生成器的发明使得Python模仿协同程序的概念得以实现。所谓协同程序，就是可以运行的独立函数调用，函数可以暂停或则
# 挂起，并在需要的时候从程序离开的地方继续或则重新开始

# 对于调用一个普通的Python函数，一般是从函数的第一行代码开始执行，结束于return语句、异常或则函数所有语句执行完毕。
# 一旦函数将控制权交给调用者，就意味着全部结束。函数中做的所有工作以及保存在局部变量中的数据都将丢失。再次调用这个函数时，一切将从头创建。

def myGen():
    print("生成器被执行！")
    yield 1
    yield 2


myG = myGen()


# print(next(myG))
# print(next(myG))
# print(next(myG))

# for i in myGen():
#     print(i)


# 斐波那契数列
def fibs():
    a = 0
    b = 1
    while True:
        a, b = b, a + b
        yield a


# for each in fibs():
#     if each > 100:
#         break
#     print(each)

a = [i for i in range(100) if not (i % 2) and i % 3]
# print(a)
# 注：python3除了有列表推导式之外，还有字典推导式：
b = {i: i % 2 == 0 for i in range(10)}
# print(b)
# 还有集合推导式
c = {i for i in [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 7, 7, 8]}
# print(c)
# 字符串推导式
d = "i for i in 'I love FishC.com!'"
# print(d)
# 注：只要在双引号内，所有的东西都变成了字符串，所以不存在字符串推导式。
e = (i for i in range(10))
# print(e)
# print(next(e))
# print(next(e))
# for each in e:
#     print(each)
# 生成器推导式如果作为函数的参数，可以直接写推导式，而不用加小括号：
a = sum(i for i in range(10) if i % 2)

# print(a)

# 解释yield和Generators（生成器）
# http://bbs.fishc.com/thread-56023-1-1.html
# return 隐含的意思是函数正将执行代码的控制返回给函数被调用的地方。
# yield 的隐含意思是控制权的转移是临时和自愿的，我们的函数将来还会收回控制权。
# （生成器（以及yield语句）最初的引入是为了让程序员可以更简单的编写用来产生值的序列的代码，以前，要实现类似随机数生成器的东西，需要实现一个类
# 或者一个模块，在生成数据的同时保持对每次调用之间状态的跟踪。）

# 有趣的素数
import math


def get_primes(input_list):
    result_list = list()
    for element in input_list:
        if is_prime(element):
            result_list.append()
    return result_list


# 或者更好一些的...
def get_primes(input_list):
    return (element for element in input_list if is_prime(element))


# 下面是is_prime的一种实现...
def is_prime(number):
    if number > 1:
        if number == 2:
            return True
        if number % 2 == 0:
            return False
        for current in range(3, int(math.sqrt(number) + 1), 2):
            if number % current == 0:
                return False
        return True
    return False


# def get_primes(start):
#     for element in magical_infinite_range(start):
#         if is_prime(element):
#             return element

